package com.gite.nouette.admin.implementation.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.gite.nouette.admin.contract.data.IJsonRpcResponse;
import com.gite.nouette.admin.contract.data.ILog;
import com.gite.nouette.admin.implementation.data.JsonRpcResponse;
import com.gite.nouette.admin.implementation.data.Log;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;

public class JsonToData {
    @SuppressWarnings("unchecked")
    public <D> D getData(JSONValue jsonValue, Meta<D> meta) {
        if (jsonValue == null) {
            throw new IllegalArgumentException("jsonValue must not be null");
        }
        if (meta == null) {
            throw new IllegalArgumentException("meta must not be null");
        }

        if (jsonValue.isNull() != null) {
            return null;
        } else if (Boolean.class.equals(meta.getClazz())) {
            return (D) new Boolean(jsonValue.isBoolean().booleanValue());
        } else if (String.class.equals(meta.getClazz())) {
            return (D) jsonValue.isString().stringValue();
        } else if (Date.class.equals(meta.getClazz())) {
            return (D) DateTimeFormat.getFormat(PredefinedFormat.RFC_2822).parse(jsonValue.isString().stringValue());
        } else if (Integer.class.equals(meta.getClazz())) {
            return (D) new Integer(jsonValue.toString());
        } else if (Long.class.equals(meta.getClazz())) {
            return (D) new Long(jsonValue.toString());
        } else if (Float.class.equals(meta.getClazz())) {
            return (D) new Float(jsonValue.toString());
        } else if (Double.class.equals(meta.getClazz())) {
            return (D) new Double(jsonValue.toString());
        } else if (List.class.equals(meta.getClazz())) {
            return (D) getDataList(jsonValue.isArray(), meta);
        } else {
            return getDataObject(jsonValue.isObject(), meta);
        }
    }

    private <D> D getData(JSONValue jsonValue, Class<D> clazz) {
        return getData(jsonValue, new Meta<D>(clazz));
    }

    @SuppressWarnings("unchecked")
    public <D> D getDataObject(JSONObject jsonObject, Meta<D> meta) {
        if (IJsonRpcResponse.class.equals(meta.getClazz())) {
            return (D) getJsonRpcResponse(jsonObject, meta);
        } else if (ILog.class.equals(meta.getClazz())) {
            return (D) getLog(jsonObject);
        }
        throw new IllegalArgumentException("class (=" + meta.getClazz() + ") not supported");
    }

    @SuppressWarnings("unchecked")
    public <D> List<D> getDataList(JSONArray jsonArray, Meta<D> meta) {
        List<D> dataList = new ArrayList<D>();
        for (int i = 0; i < jsonArray.size(); i++) {
            D data = (D) getData(jsonArray.get(i), meta.getMeta(""));
            dataList.add(data);
        }
        return dataList;
    }

    @SuppressWarnings("unchecked")
    public <R> IJsonRpcResponse<R> getJsonRpcResponse(JSONObject jsonObject, Meta<R> meta) {
        IJsonRpcResponse<R> jSonRpcResponse = new JsonRpcResponse<R>();
        jSonRpcResponse.setJsonrpc(getData(jsonObject.get("jsonrpc"), String.class));
        jSonRpcResponse.setResult((R) getData(jsonObject.get("result"), meta.getMeta("result")));
        JSONValue error;
        if ((error = jsonObject.get("error")) != null) {
            jSonRpcResponse.setError(getData(error, String.class));
        }
        jSonRpcResponse.setId(getData(jsonObject.get("id"), String.class));

        return jSonRpcResponse;
    }

    public ILog getLog(JSONValue jsonValue) {
        JSONObject jsonObject = jsonValue.isObject();
        ILog log = new Log();
        log.setId(getData(jsonObject.get("id"), Integer.class));
        log.setMessage(getData(jsonObject.get("message"), String.class));
        log.setTimestamp(getData(jsonObject.get("timestamp"), Date.class));
        log.setUsername(getData(jsonObject.get("username"), String.class));

        return log;
    }
}
